package com.linser.gistools.common.datastore;

import com.linser.gistools.common.datastore.model.BaseDataStoreModel;
import com.linser.gistools.common.datastore.model.DbInfo;
import com.linser.gistools.common.datastore.model.MysqlDataStoreModel;
import com.linser.gistools.utils.DialogUtils;
import com.linser.gistools.utils.EventBusUtil;
import com.linser.gistools.utils.GlobalVariables;
import com.linser.gistools.utils.VertxUtils;
import com.linser.gistools.utils.busevents.RefreshDataBaseExploreEvent;
import com.linser.gistools.utils.enums.NotificationType;
import io.vertx.core.file.FileSystem;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import javafx.geometry.Pos;
import org.geotools.data.DataAccessFactory;
import org.geotools.data.DataStore;
import org.geotools.data.db2.DB2NGDataStoreFactory;
import org.geotools.data.h2.H2DataStoreFactory;
import org.geotools.data.mysql.MySQLDataStoreFactory;
import org.geotools.data.oracle.OracleNGDataStoreFactory;
import org.geotools.data.postgis.PostgisNGDataStoreFactory;
import org.geotools.data.sqlserver.jtds.JTDSSqlServerDataStoreFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author: linser
 * @Desc: 数据源工厂类
 * @create: 2024-02-01 14:02
 **/
public class DataStoreFactory {

    //数据库类型存储对象
    public static Map<String, Map<String, BaseDataStoreModel>> dbTypeClientsMap = new ConcurrentHashMap<>();
    private static Logger logger = LoggerFactory.getLogger(DataStoreFactory.class);
    /**
     * 创建db2数据源连接参数
     * @return dataStoreParams
     */
    public static Map<String, Object> createDB2StoreParams(DbInfo dbInfo) {
        // 数据库连接参数
        Map<String, Object> params = new HashMap<>();
        params.put(DB2NGDataStoreFactory.DBTYPE.key, dbInfo.getDbType().sample);
        params.put(DB2NGDataStoreFactory.HOST.key, dbInfo.getHost());
        params.put(DB2NGDataStoreFactory.PORT.key, dbInfo.getPort());
        params.put(DB2NGDataStoreFactory.DATABASE.key, dbInfo.getDatabase());
        params.put(DB2NGDataStoreFactory.USER.key, dbInfo.getUsername());
        params.put(DB2NGDataStoreFactory.PASSWD.key, dbInfo.getPassword());
        return params;
    }

    /**
     * 创建h2数据源连接参数
     * @return dataStoreParams
     */
    public static Map<String, Object> createH2StoreParams(DbInfo dbInfo) {
        // 数据库连接参数
        Map<String, Object> params = new HashMap<>();
        params.put(H2DataStoreFactory.DBTYPE.key, dbInfo.getDbType().sample);
        params.put(H2DataStoreFactory.HOST.key, dbInfo.getHost());
        params.put(H2DataStoreFactory.PORT.key, dbInfo.getPort());
        params.put(H2DataStoreFactory.DATABASE.key, dbInfo.getDatabase());
        params.put(H2DataStoreFactory.USER.key, dbInfo.getUsername());
        params.put(H2DataStoreFactory.PASSWD.key, dbInfo.getPassword());
        params.put(H2DataStoreFactory.MAXWAIT.key, "10");
        return params;
    }

    /**
     * 创建mysql数据源连接参数
     * @return dataStoreParams
     */
    public static Map<String, Object> createMySQLStoreParams(DbInfo dbInfo) {
        // 数据库连接参数
        Map<String, Object> params = new HashMap<>();
        params.put(MySQLDataStoreFactory.DBTYPE.key, dbInfo.getDbType().sample);
        params.put(MySQLDataStoreFactory.HOST.key, dbInfo.getHost());
        params.put(MySQLDataStoreFactory.PORT.key, dbInfo.getPort());
        params.put(MySQLDataStoreFactory.DATABASE.key, dbInfo.getDatabase());
        params.put(MySQLDataStoreFactory.USER.key, dbInfo.getUsername());
        params.put(MySQLDataStoreFactory.PASSWD.key, dbInfo.getPassword());
        params.put(MySQLDataStoreFactory.MAXWAIT.key, "10");
        //设置InnoDB使其支持空间索引
        params.put(MySQLDataStoreFactory.STORAGE_ENGINE.key, "InnoDB");

        params.put("InnoDB", "true"); // 设置使用 InnoDB 存储引擎
        return params;
    }

    /**
     * 创建mysql数据源连接参数
     * @return dataStoreParams
     */
    public static Map<String, Object> createPostgresStoreParams(DbInfo dbInfo) {
        // 数据库连接参数
        Map<String, Object> params = new HashMap<>();
        params.put(PostgisNGDataStoreFactory.DBTYPE.key, dbInfo.getDbType().sample);
        params.put(PostgisNGDataStoreFactory.HOST.key, dbInfo.getHost());
        params.put(PostgisNGDataStoreFactory.PORT.key, dbInfo.getPort());
        params.put(PostgisNGDataStoreFactory.SCHEMA.key, dbInfo.getSchema());
        params.put(PostgisNGDataStoreFactory.DATABASE.key, dbInfo.getDatabase());
        params.put(PostgisNGDataStoreFactory.USER.key, dbInfo.getUsername());
        params.put(PostgisNGDataStoreFactory.PASSWD.key, dbInfo.getUsername());
        return params;
    }



    /**
     * 创建Oracle数据源连接参数
     * @return dataStoreParams
     */
    public static Map<String, Object> createOracleStoreParams(DbInfo dbInfo) {
        // 数据库连接参数
        Map<String, Object> params = new HashMap<>();
        params.put(OracleNGDataStoreFactory.DBTYPE.key, dbInfo.getDbType().sample);
        params.put(OracleNGDataStoreFactory.HOST.key, dbInfo.getHost());
        params.put(OracleNGDataStoreFactory.PORT.key, dbInfo.getPort());
        params.put(OracleNGDataStoreFactory.DATABASE.key, dbInfo.getDatabase());
        params.put(OracleNGDataStoreFactory.SCHEMA.key, dbInfo.getSchema());
        params.put(OracleNGDataStoreFactory.USER.key, dbInfo.getUsername());
        params.put(OracleNGDataStoreFactory.PASSWD.key, dbInfo.getPassword());
        return params;
    }

    /**
     * 创建SQLServer数据源连接参数
     * @return dataStoreParams
     */
    public static Map<String, Object> createSQLServerStoreParams(DbInfo dbInfo) {
        // 数据库连接参数
        Map<String, Object> params = new HashMap<>();
        params.put(JTDSSqlServerDataStoreFactory.DBTYPE.key, JTDSSqlServerDataStoreFactory.DBTYPE.type);
        params.put(JTDSSqlServerDataStoreFactory.HOST.key, dbInfo.getHost());
        params.put(JTDSSqlServerDataStoreFactory.PORT.key, dbInfo.getPort());
        params.put(JTDSSqlServerDataStoreFactory.DATABASE.key, dbInfo.getDatabase());
        params.put(JTDSSqlServerDataStoreFactory.SCHEMA.key, dbInfo.getSchema());
        params.put(JTDSSqlServerDataStoreFactory.USER.key, dbInfo.getUsername());
        params.put(JTDSSqlServerDataStoreFactory.PASSWD.key, dbInfo.getPassword());
        return params;
    }


    /**
     * 添加库连接
     * @param dbInfo 库连接信息
     * @return
     */
    public static boolean addDataStore(DbInfo dbInfo,boolean refreshConfigFile) {
        String uuid = dbInfo.getUuid();
        String dbType = String.valueOf(dbInfo.getDbType().sample);
        Map<String, BaseDataStoreModel> currentTypeDbClientsMap;
        if(dbTypeClientsMap.containsKey(dbType)){
            currentTypeDbClientsMap = dbTypeClientsMap.get(dbType);
        }else {
            currentTypeDbClientsMap = new ConcurrentHashMap<>();
            dbTypeClientsMap.put(dbType,currentTypeDbClientsMap);
        }
        //判断是否已经存在
        if (currentTypeDbClientsMap.containsKey(uuid)) {
            logger.error("重复添加！ uuid:{}", uuid);
            return false;
        }
        Map<String, Object> localConnectionDataMap = new HashMap<>();
        if(dbInfo.getDbType() == DB2NGDataStoreFactory.DBTYPE){
            localConnectionDataMap = createDB2StoreParams(dbInfo);
            BaseDataStoreModel baseDataStoreModel = new BaseDataStoreModel(dbInfo,localConnectionDataMap);
            currentTypeDbClientsMap.put(baseDataStoreModel.getUuid(), baseDataStoreModel);
        } else if (dbInfo.getDbType() == H2DataStoreFactory.DBTYPE) {
            localConnectionDataMap = createH2StoreParams(dbInfo);
            BaseDataStoreModel baseDataStoreModel = new BaseDataStoreModel(dbInfo,localConnectionDataMap);
            currentTypeDbClientsMap.put(baseDataStoreModel.getUuid(), baseDataStoreModel);
        }else if (dbInfo.getDbType() == MySQLDataStoreFactory.DBTYPE) {
            localConnectionDataMap = createMySQLStoreParams(dbInfo);
            MysqlDataStoreModel mysqlDataStoreModel = new MysqlDataStoreModel(dbInfo,localConnectionDataMap);
            currentTypeDbClientsMap.put(mysqlDataStoreModel.getUuid(), mysqlDataStoreModel);
        }else if (dbInfo.getDbType() == PostgisNGDataStoreFactory.DBTYPE) {
            localConnectionDataMap = createPostgresStoreParams(dbInfo);
            BaseDataStoreModel baseDataStoreModel = new BaseDataStoreModel(dbInfo,localConnectionDataMap);
            currentTypeDbClientsMap.put(baseDataStoreModel.getUuid(), baseDataStoreModel);
        }else if (dbInfo.getDbType() == OracleNGDataStoreFactory.DBTYPE) {
            localConnectionDataMap = createOracleStoreParams(dbInfo);
            BaseDataStoreModel baseDataStoreModel = new BaseDataStoreModel(dbInfo,localConnectionDataMap);
            currentTypeDbClientsMap.put(baseDataStoreModel.getUuid(), baseDataStoreModel);
        }else if (dbInfo.getDbType() == JTDSSqlServerDataStoreFactory.DBTYPE) {
            localConnectionDataMap = createSQLServerStoreParams(dbInfo);
            BaseDataStoreModel baseDataStoreModel = new BaseDataStoreModel(dbInfo,localConnectionDataMap);
            currentTypeDbClientsMap.put(baseDataStoreModel.getUuid(), baseDataStoreModel);
        }

        logger.debug("成功添加 {} 到数据库连接中", uuid);
        if(refreshConfigFile){
            refreshDbTypeClientsDataToFile();
        }
        return true;
    }
    public static void removeDataStore(String dbType, String clientUuid) {
        if(dbTypeClientsMap.containsKey(dbType)){
            Map<String, BaseDataStoreModel>  currentTypeDbClientsMap = dbTypeClientsMap.get(dbType);
            if (!currentTypeDbClientsMap.containsKey(clientUuid)) {
                DialogUtils.showNotification("目标库不存在！", Pos.BOTTOM_RIGHT, NotificationType.ERROR);
                return;
            }
            BaseDataStoreModel baseDataStoreModel = currentTypeDbClientsMap.get(clientUuid);
            DataStore dateStore =  baseDataStoreModel.getDataStore();
            if(dateStore != null){
                dateStore.dispose();
            }
            currentTypeDbClientsMap.remove(clientUuid);
            refreshDbTypeClientsDataToFile();
            logger.debug("成功移除 {} 库", clientUuid);
        }else {
            logger.debug("目标库类型 {} 不存在!", dbType);
        }
    }

    /**
     * 关闭所有连接
     * @param closeAndRemove 是否在关闭的同时移除
     */
    public static void closeAllClient(boolean closeAndRemove) {
        dbTypeClientsMap.forEach((typeKey,typeValue)->{
            typeValue.forEach((ket,value)->{
                DataStore dateStore =  value.getDataStore();
                if(dateStore != null){
                    dateStore.dispose();
                }
            });
            if(closeAndRemove){
                typeValue.clear();
            }
        });
        logger.debug("成功关闭全部数据库连接！");
    }

    public static void refreshDbTypeClientsDataToFile(){
        JsonArray dbTypeArray = new JsonArray();
        FileSystem fs = VertxUtils.getFileSystem();
        dbTypeClientsMap.forEach((typeKey,typeValue)->{
            JsonObject dataTypeJson = new JsonObject();
            JsonArray dataClientArray = new JsonArray();
            typeValue.forEach((ket,value)->{
                JsonObject dbClientJson = new JsonObject();
                dbClientJson.put("uuid",value.getDbInfo().getUuid());
                dbClientJson.put("name",value.getDbInfo().getName());
                dbClientJson.put("host",value.getDbInfo().getHost());
                dbClientJson.put("port",value.getDbInfo().getPort());
                dbClientJson.put("dBTyp",value.getDbInfo().getDbType().sample);
                dbClientJson.put("schema",value.getDbInfo().getSchema());
                dbClientJson.put("username",value.getDbInfo().getUsername());
                dbClientJson.put("password",value.getDbInfo().getPassword());
                dbClientJson.put("database",value.getDbInfo().getDatabase());
                dbClientJson.put("comment",value.getDbInfo().getComment());
                dataClientArray.add(dbClientJson);
            });
            dataTypeJson.put("dbType",typeKey);
            dataTypeJson.put("dbClients",dataClientArray);
            dbTypeArray.add(dataTypeJson);
        });
        String dbPreferencePath = Objects.requireNonNull(DataStoreFactory.class.getResource("/" + GlobalVariables.mainPackageName + "/config/db_preference.json")).getPath();
        fs.writeFile(dbPreferencePath, dbTypeArray.toBuffer(), result -> {
            if (result.succeeded()) {
                // 日志输出写入文件成功的信息
                logger.debug("成功写入文件: " + dbPreferencePath);
            } else {
                // 日志输出写入文件失败的信息
                logger.error("写入文件失败: " + result.cause().getMessage());
            }
        });
    }

    public static BaseDataStoreModel getDataStoreModelByTypeAndUuid(String dbType, String clientUuid){
        if(dbTypeClientsMap.containsKey(dbType)){
            Map<String, BaseDataStoreModel>  currentTypeDbClientsMap = dbTypeClientsMap.get(dbType);
            if (!currentTypeDbClientsMap.containsKey(clientUuid)) {
                DialogUtils.showNotification("目标库不存在！", Pos.BOTTOM_RIGHT, NotificationType.ERROR);
                return null;
            }
            return currentTypeDbClientsMap.get(clientUuid);
        }else {
            logger.debug("目标库类型 {} 不存在!", dbType);
            return null;
        }
    }

    public static void getDbTypeClientsMap(){
        FileSystem fs = VertxUtils.getFileSystem();
        String dbPreferencePath = Objects.requireNonNull(DataStoreFactory.class.getResource("/" + GlobalVariables.mainPackageName + "/config/db_preference.json")).getPath();
        fs.readFile(dbPreferencePath, result ->{
            if (result.succeeded()) {
                dbTypeClientsMap = new ConcurrentHashMap<>();
                if(result.result().length() != 0){
                    // 成功读取文件
                    JsonArray dbTypeClientsJson = new JsonArray(result.result());
                    Iterator iterator = dbTypeClientsJson.iterator();
                    while (iterator.hasNext()) {
                        JsonObject jsonObject = (JsonObject) iterator.next();
                        if(jsonObject.containsKey("dbType")  && jsonObject.containsKey("dbClients")){
                            String dbType = jsonObject.getString("dbType");
                            DataAccessFactory.Param dbTypeParam = getParam(dbType);
                            JsonArray dbClientsJsonArray = jsonObject.getJsonArray("dbClients");
                            Iterator dbClientIterator = dbClientsJsonArray.iterator();
                            while (dbClientIterator.hasNext()){
                                JsonObject dbInfoJsonObject = (JsonObject) dbClientIterator.next();
                                DbInfo dbInfo = new DbInfo();
                                dbInfo.setUuid(dbInfoJsonObject.getString("uuid"));
                                dbInfo.setName(dbInfoJsonObject.getString("name"));
                                dbInfo.setHost(dbInfoJsonObject.getString("host"));
                                dbInfo.setPort(Integer.valueOf(dbInfoJsonObject.getString("port")));
                                dbInfo.setDBType(dbTypeParam);
                                dbInfo.setDatabase(dbInfoJsonObject.getString("database"));
                                dbInfo.setUsername(dbInfoJsonObject.getString("username"));
                                dbInfo.setPassword(dbInfoJsonObject.getString("password"));
                                dbInfo.setComment(dbInfoJsonObject.getString("comment"));
                                addDataStore(dbInfo,false);
                            }
                        }
                    }

                }
                EventBusUtil.getDefault().post(new RefreshDataBaseExploreEvent());
                logger.debug("读取数据库配置文件中！");
            } else {
                // 日志输出写入文件失败的信息
                System.out.println("文件读取失败！: ");
            }
        });
    }

    private static DataAccessFactory.Param getParam(String dbType) {
        DataAccessFactory.Param dbTypeParam = null;
        if(Objects.equals(dbType, DB2NGDataStoreFactory.DBTYPE.sample.toString())){
            dbTypeParam = DB2NGDataStoreFactory.DBTYPE;
        } else if (Objects.equals(dbType, H2DataStoreFactory.DBTYPE.sample.toString())) {
            dbTypeParam = H2DataStoreFactory.DBTYPE;
        }else if (Objects.equals(dbType, MySQLDataStoreFactory.DBTYPE.sample.toString())) {
            dbTypeParam = MySQLDataStoreFactory.DBTYPE;
        }else if (Objects.equals(dbType, PostgisNGDataStoreFactory.DBTYPE.sample.toString())) {
            dbTypeParam = PostgisNGDataStoreFactory.DBTYPE;
        }else if (Objects.equals(dbType, OracleNGDataStoreFactory.DBTYPE.sample.toString())) {
            dbTypeParam = OracleNGDataStoreFactory.DBTYPE;
        }else if (Objects.equals(dbType, JTDSSqlServerDataStoreFactory.DBTYPE.sample.toString())) {
            dbTypeParam = JTDSSqlServerDataStoreFactory.DBTYPE;
        }
        return dbTypeParam;
    }

}
